package cn.edu.suda.ada.database.dao;

import cn.edu.suda.ada.database.exception.DatabaseException;
import cn.edu.suda.ada.database.objects.Column;
import cn.edu.suda.ada.database.objects.Key;
import cn.edu.suda.ada.database.objects.Table;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * 高级数据库操作类
 * 
 * @author JiangJun
 *
 */
public class AdvModel extends Model {
	/**
	 * 数据表结构
	 */
	private Table table;
	/**
	 * 当前操作的数据库名称
	 */
	private String databaseName;
	{
		databaseName = getDatabaseName();
	}
	
	public AdvModel() throws DatabaseException {
		super();
	}

	public AdvModel(String path) throws DatabaseException {
		super(path);
	}
	
	public AdvModel(String path, String tableName) throws DatabaseException {
		super(path, tableName);
	}
	
	public AdvModel(String path, String tableName, String key) throws DatabaseException {
		super(path, tableName, key);
	}
	
	public AdvModel(String path, String tableName, String key, String databaseName) throws DatabaseException {
		super(path, tableName, key);
		setCurrentDatabaseName(databaseName);
	}

	public void setCurrentDatabaseName(String databaseName) {
		this.databaseName = databaseName;
	}
	
	/**
	 * 获得当前操作的数据库名称
	 * 
	 * @return 当前数据库名称
	 */
	public String getCurrentDatabaseName() {
		return databaseName;
	}
	
	public Table getTable() {
		return table;
	}

	public void setTable(Table table) {
		this.table = table;
		this.table.setName(getTablePrefix() + table.getName());
		// 同时设置表名
		setTableName(table.getName());
	}

	/**
	 * 删除当前表
	 * 
	 * @return 是否执行成功
	 * @throws DatabaseException 数据库错误
	 */
	public boolean drop() throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.dropTable(tableName);
		return this.execute(sql);
	}

	/**
	 * 删除后新增当前表
	 * 
	 * @return 是否创建成功
	 * @throws DatabaseException 数据库错误 
	 */
	public boolean dropAndCreate() throws DatabaseException {
		this.drop();
		return this.create();
	}

	/**
	 * 创建数据表
	 * 
	 * @return 是否创建成功
	 * @throws DatabaseException 数据库错误
	 */
	public boolean create() throws DatabaseException {
		// 必须设置表结构
		if (null == table) {
			throw new DatabaseException("必须先设置表结构！");
		}

		sql = dbParser.createTable(table);
		return this.execute(sql);
	}

	/**
	 * 获得当前表信息
	 * 
	 * @return 数据表对象
	 * @throws DatabaseException 数据库错误
	 */
	public Table table() throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		if (null == databaseName) {
			throw new DatabaseException("无法获得操作的数据库名称！");
		}
		sql = dbParser.showTable(tableName, databaseName);
		if (null == sql) throw new DatabaseException("无法解析数据库语句！");
		this.log();

		List<Map<String, Object>> tableList = getResultList();
		
		if (tableList.isEmpty()) {
			return null;
		} else {
			Map<String, Object> map = tableList.get(0);
			table = new Table();
			table = dbParser.mapToTable(map);
			table.setColumns(columns());
			table.setKeys(keys());
		}
		return table;
	}
	
	/**
	 * 获得所有列
	 * 
	 * @return 数据列对象集合
	 * @throws DatabaseException 数据库错误
	 */
	public Set<Column<?>> columns() throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.showColumns(tableName);
		if (null == sql) throw new DatabaseException("无法解析数据库语句！");
		this.log();

		List<Map<String, Object>> columnsList = getResultList();
		
		Set<Column<?>> columns = new LinkedHashSet<Column<?>>();
		for (Map<String, Object> map : columnsList) {
			columns.add(dbParser.mapToColumn(map));
		}
		return columns;
	}

	/**
	 * 获得所有索引
	 * 
	 * @return 索引对象集合
	 * @throws DatabaseException 数据库错误 
	 */
	public Set<Key> keys() throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.showKeys(tableName);
		if (null == sql) throw new DatabaseException("无法解析数据库语句！");
		this.log();
		
		List<Map<String, Object>> keysList = getResultList();
		
		Set<Key> keys = new HashSet<Key>();
		for (Map<String, Object> map : keysList) {
			Key key = dbParser.mapToKey(map);
			boolean exist = false;
			for (Key tmp : keys) {
				if (tmp.getName().equals(key.getName())) {
					exist = true;
					for (String column : key.getColumns()) {
						tmp.addColumn(column);
					}
					break;
				}
			}
			// 该索引不存在时添加
			if (!exist) {
				keys.add(key);
			}
		}
		return keys;
	}

	/**
	 * 获得当前数据表的所有列名
	 * 
	 * @return 字段名称集合
	 * @throws DatabaseException 数据库错误 
	 */
	public Set<String> fields() throws DatabaseException {
		Set<String> fields = new LinkedHashSet<String>();
		Set<Column<?>> columns = columns();
		if (null != columns) {
			for (Column<?> column : columns) {
				fields.add("`" + column.getName() + "`");
			}
		}
		return fields;
	}

	/**
	 * 修改列
	 * 
	 * @param oldColumn 原数据列对象
	 * @param newColumn 新数据列对象
	 * @return 是否修改成功
	 * @throws DatabaseException 数据库错误
	 */
	public boolean alterColumn(Column<?> oldColumn, Column<?> newColumn) throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.alterColumn(tableName, oldColumn, newColumn);
		return this.execute(sql);
	}

	/**
	 * 删除列
	 * 
	 * @param columnName 数据列名称
	 * @return 是否删除成功
	 * @throws DatabaseException 数据库错误
	 */
	public boolean dropColumn(String columnName) throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.dropColumn(tableName, columnName);
		return this.execute(sql);
	}

	/**
	 * 新增列
	 * 
	 * @param column 数据列对象
	 * @return 是否新增成功
	 * @throws DatabaseException 数据库错误 
	 */
	public boolean addColumn(Column<?> column) throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置具体的数据表名称！");
		}
		sql = dbParser.addColumn(tableName, column);
		return this.execute(sql);
	}

	/**
	 * 拷贝表数据
	 * 
	 * @param newTableName 新数据表名称
	 * @return 是否拷贝成功
	 * @throws DatabaseException 数据库错误
	 */
	public boolean copyTableStruct(String newTableName) throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置原始数据表名称！");
		}
		if (null == newTableName) {
			throw new DatabaseException("请设置新数据表名称！");
		}
		sql = dbParser.copyTableStructure(tableName, getRealTableName(newTableName));
		return this.execute(sql);
	}

	/**
	 * 拷贝表结构及数据
	 * 
	 * @param newTableName 新数据表名称
	 * @return 是否拷贝成功
	 * @throws DatabaseException 数据库错误 
	 */
	public boolean copyTable(String newTableName) throws DatabaseException {
		if (!this.copyTableStruct(getRealTableName(newTableName))) {
			return false;
		}
		sql = dbParser.copyTable(tableName, getRealTableName(newTableName));
		return this.execute(sql);
	}
	
	/**
	 * 检查表是否存在
	 * 
	 * @return 是否存在
	 * @throws DatabaseException 数据库错误 
	 */
	public boolean exist() throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请先设置数据表名称！");
		}
		return exist(tableName);
	}
	
	/**
	 * 检查表是否存在
	 * 
	 * @param tableName 数据表名称
	 * @return 是否存在
	 * @throws DatabaseException 数据库错误 
	 */
	public boolean exist(String tableName) throws DatabaseException {
		if (null == tableName) {
			throw new DatabaseException("请设置查询的数据表名称！");
		}
		sql = dbParser.exist(tableName);
		if (null == sql) throw new DatabaseException("无法解析数据库语句！");
		this.log();
		
		try {
			ResultSet rs = db.executeQuery(sql);
			if (rs.next()) {
				return rs.getInt(1) > 0;
			}
		} catch (SQLException e) {
			throw new DatabaseException(e);
		} finally {
			close();
		}
		return false;
	}

	/**
	 * 调用执行数据库操作的方法
	 * 
	 * @param sql 执行语句
	 * @return 是否执行成功
	 * @throws DatabaseException 数据库错误
	 */
	protected boolean execute(String sql) throws DatabaseException {
		if (null == sql || sql.isEmpty()) {
			throw new DatabaseException("无法解析数据库语句！");
		}
		this.log();
		try {
			return db.execute(sql);
		} catch (SQLException e) {
			throw new DatabaseException(e);
		} finally {
			close();
		}
	}
}